package com.zhuhjay.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory;

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

/**
 * @author ZhuHJay
 * @date 2023/1/11 20:28
 */
@Slf4j
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class GlobalConfig {

    /** 开启 @Validate 注解, 提供参数校验功能 **/
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor(){
        return new MethodValidationPostProcessor();
    }

    /** 配置 Spring Validation **/
    @Bean
    public Validator validator(AutowireCapableBeanFactory springFactory) {
        try (ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
                .configure()
                // 快速失败，默认情况下校验所有字段再抛出异常
                .failFast(true)
                // 解决 SpringBoot 依赖注入问题
                .constraintValidatorFactory(new SpringConstraintValidatorFactory(springFactory))
                .buildValidatorFactory()) {
            return factory.getValidator();
        }
    }

    /** 项目启动完成后打印一些常用的地址方便启用 **/
    @Bean
    public ApplicationRunner consoleLogRunner() {
        return arg ->
            log.info(
                    "\n-------------------SpringBoot启动完成-------------------\n" +
                      "提供以下访问路径:\n" +
                      "    API文档:\t{}\n" +
                      "    作者   :\t{}\n" +
                      "-------------------SpringBoot启动完成-------------------\n",
                    "http://localhost/doc.html",
                    "@ZhuHJay"
            );
    }

    /** 解决 LocalDateTime 序列化和反序列化问题 **/
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            // LocalDateTime 序列化和反序列化
            builder.serializerByType(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
                @Override
                public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    if (value != null) {
                        long timestamp = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                        gen.writeNumber(timestamp);
                    }
                }
            }).deserializerByType(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
                @Override
                public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
                        throws IOException {
                    long timestamp = p.getValueAsLong();
                    if (timestamp > 0) {
                        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
                    }
                    return null;
                }
            });
        };
    }

}
